﻿using System;
using System.Collections.Generic;
using DimOS.Constants;
using DimOS.Internals;
using DimOS.Plugs;
using Convert = DimOS.Plugs.Convert;

namespace DimOS.H
{
    public class Value
    {
        public static Value Empty = new Value();

        public ValueType Type { get; set; }

        public bool Return { get; set; }

        private float _number;
        public float Number { get { return _number; }
        set { _number = value; _string = ""; _list
        .Clear(); Type = ValueType.Number; } }

        private string _string = "";
        public string String { get { return _string; }
        set { _string = value; _number = 0; _list
        .Clear(); Type = ValueType.String; } }

        private List<Value> _list = new List<Value>();
        public List<Value> List { get { return _list; }
        set { _list = value; _number = 0; _string = "";
        Type = ValueType.List; } }

        public string Action { get; set; }

        private List<Entry> _hashedValues = new List<Entry>();

        public Value()
        { _number = 0; _string = ""; _list.Clear(); Action = null; Type
          = ValueType.None; _hashedValues.Clear(); Return = false; }

        public Value(ValueType value) { Type = value; }

        public Value(float value) { Number = value; }

        public Value(bool value) { Number = value ? 1 : 0; }

        public Value(string value) { String = value; }

        public Value(List<Value> value) { List = value.Clone(); }

        public Value(Value value)
        {
            Action = value.Action; Type = value.Type; _hashedValues = value._hashedValues; _number = value
            .Type == ValueType.Number ? value.Number : 0; _string = value.Type == ValueType.String ? value
            .String : ""; if (value.Type == ValueType.List) _list = value.List.Clone(); else _list.Clear();
            Return = value.Return;
        }

        public override string ToString()
        {
            if (Type == ValueType.Number) return Number.ToString(); if (Type ==
            ValueType.String) return String; if (Type == ValueType.None) return
            ""; if (List.Count == 0) return Tokens.ValueLeft + Tokens.ValueRight
            .ToString(); var result = Tokens.ValueLeft.ToString(); for (int i =
            0; i < List.Count; i++) result += (List[i].Type != ValueType.String
            ? List[i].ToString() : Tokens.VerbatimString + List[i].ToString() +
            Tokens.VerbatimString) + Tokens.Separator; result = result.Remove
            (result.Length - 1, 1) + Tokens.ValueRight; return result;
        }

        public Value Clone()
        { return new Value { Action = Action, Type = Type, _number = Type == ValueType.Number ?
          Number : 0, _string = Type == ValueType.String ? String : "", _list = Type == ValueType
          .List ? List.Clone() : new List<Value>(), _hashedValues = _hashedValues, Return = Return }; }

        public int AddElement(string hash, Value item)
        {
            var entry = Entry.Find(_hashedValues, hash);
            if (entry != null) { _list[entry.Value] =
            item; return entry.Value; } _list.Add(item);
            _hashedValues.Add(new Entry(hash, _list
            .Count - 1)); return _list.Count - 1;
        }

        public int ElementIndex(Value index)
        {
            if (Type != ValueType.List) return -1; if (index.Type == ValueType
            .Number) return Convert.ToInt32(index.Number.Round()); var entry =
            Entry.Find(_hashedValues, index.ToString()); if (entry != null &&
            entry.Value  < List.Count) return entry.Value; return -1;
        }

        public bool ElementExists(Value specifier, bool valueNotEmpty = true)
        {
            if (Type != ValueType.List) return false; if (specifier.Type == ValueType.Number)
            { var index = Convert.ToInt32(specifier.Number.Round()); if (index < 0) index =
            List.Count - System.Math.Abs(index); if (index < 0 || index >= List.Count) return
            false; if (valueNotEmpty) return List[index].Type != ValueType.None; return true; }
            return Entry.Find(_hashedValues, specifier.ToString()) != null;
        }

        public Value this[int index]
        { get { var count = Type == ValueType.List ? List.Count : 1; if (index < 0)
          index = count - System.Math.Abs(index); if (index < 0 || index >= count)
          throw new InvalidOperationException("Index (" + index + ") was out of " +
          "range [" + count + "]."); if (Type == ValueType.List) return List[index];
          return this; } }

        public Value this[List<Value> specifiers]
        { get { var temp = this; for (int i = 0; i < specifiers.Count; i++) { var index
          = temp.ElementIndex(specifiers[i]); var count = temp.List != null ? temp.List
          .Count : 0; if (index < 0) index = count - System.Math.Abs(index); if (index < 0
          || index >= count) { throw new InvalidOperationException("Index (" + specifiers
          [i] + ") was out of range [" + count + "]."); } if (temp.List != null) temp =
          temp.List[index]; } return temp; } }

        public static List<Value> ElementSpecifiers(ref string name, ref int end)
        {
            var specifiers = new List<Value>(); var start = name.IndexOf
            (Tokens.ValueLeft); if (start < 0) return specifiers;

            while (start < name.Length && name[start] == Tokens.ValueLeft)
            { end = name.IndexOf(Tokens.ValueRight, start + 1); if (end == -1
              || end <= start + 1) break; var temp = new Script(name, start);
              if (temp.Valid && (temp.Data[temp.Pointer] == Tokens.ParameterLeft
              || temp.Data[temp.Pointer] == Tokens.ValueLeft)) temp.Traverse(1);
              specifiers.Add(temp.Execute(Tokens.ValueRight)); start = end + 1; }
              if (specifiers.Count > 0) { name = name.Substring(0, name.IndexOf
              (Tokens.ValueLeft)); end = start - 1; } return specifiers;
        }

        public static List<Value> ElementSpecifiers(ref string name)
        { var end = 0; return ElementSpecifiers(ref name, ref end); }
    }
}
